use crate::periph2::rcc::{self, RccAxB};

use super::time_delay::Timer;
use darling::{FromDeriveInput, FromMeta};
use proc_macro::TokenStream;
use proc_macro2::TokenStream as T2;
use quote::{format_ident, quote};
use syn::{parse_macro_input, DeriveInput};

#[derive(Debug, FromDeriveInput)]
#[darling(attributes(pwm))]
pub struct PwmConfig2 {
    tim: Timer,
    psc: u16,
    arr: u16,
    init: String,
    #[darling(multiple)]
    ch: Vec<PwmAttr2>,
}

#[derive(Debug, FromMeta)]
struct PwmAttr2 {
    ch: u8,
    mode: u8,
    name: String,
}

impl PwmConfig2 {
    fn build(&self) -> T2 {
        let name = self.tim.name();
        let rcc_field = format_ident!("{}en", name);
        let init_fn = format_ident!("{}", self.init);
        let name = format_ident!("{}", name.to_uppercase());
        let rcc_src = if self.tim.is_advanced() {
            rcc::rcc_enable(RccAxB::APB2, &rcc_field, false)
        } else {
            rcc::rcc_enable(RccAxB::APB1, &rcc_field, false)
        };
        let mut ccmr1 = Vec::new();
        let mut ccmr2 = Vec::new();
        let mut ccer = Vec::new();
        let mut ch_src = quote! {};
        let mut arr_fn = quote! {};

        for ch in self.ch.iter() {
            let ccr = format_ident!("ccr{}", ch.ch);
            ch_src.extend(quote! {
                pwm.#ccr().write(|w| w.ccr().variant(0));
            });
            let ccs = format_ident!("cc{}s", ch.ch);
            let ocfe = format_ident!("oc{}fe", ch.ch);
            let ocpe = format_ident!("oc{}pe", ch.ch);
            let ocm = format_ident!("oc{}m", ch.ch);
            let occe = format_ident!("oc{}ce", ch.ch);
            let pwm_mode = format_ident!("pwm_mode{}", ch.mode);
            let ccmr = quote! {
                .#ccs().output()
                .#ocfe().clear_bit()
                .#ocpe().disabled()
                .#ocm().#pwm_mode()
                .#occe().clear_bit()
            };
            if ch.ch <= 2 {
                ccmr1.push(ccmr);
            } else {
                ccmr2.push(ccmr);
            }
            let cce = format_ident!("cc{}e", ch.ch);
            ccer.push(cce);

            let arr_fn_name = format_ident!("set_{}_ccr", ch.name);
            arr_fn.extend(quote! {
                #[inline(always)]
                pub fn #arr_fn_name(value: u16) {
                    let pwm: stm32f1::stm32f103::#name = unsafe { ::core::mem::transmute(()) };
                    pwm.#ccr().write(|w| w.ccr().variant(value));
                }
            });
        }

        if !ccmr1.is_empty() {
            ch_src.extend(quote! {
                pwm.ccmr1_output().write(|w| w #(#ccmr1)*);
            });
        }
        if !ccmr2.is_empty() {
            ch_src.extend(quote! {
                pwm.ccmr2_output().write(|w| w #(#ccmr2)*);
            });
        }

        let psc = self.psc;
        let arr = self.arr;
        quote! {
            pub fn #init_fn() {
                #rcc_src
                let pwm: stm32f1::stm32f103::#name = unsafe { ::core::mem::transmute(()) };
                pwm.cr1.reset();
                pwm.cr2.reset();
                pwm.psc.write(|w| w.psc().variant(#psc));
                pwm.arr.write(|w| w.arr().variant(#arr));
                #ch_src
                pwm.ccer.write(|w| w #(.#ccer().set_bit())*);
                pwm.egr.write(|w| w.ug().update());
                pwm.cr1.write(|w| w.cen().enabled());
            }
            #arr_fn
        }
    }
}

pub fn build(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = &input.ident;
    let config = PwmConfig2::from_derive_input(&input).unwrap();
    let build = config.build();
    let expand = quote! {
        impl #name {
            #build
        }
    };
    expand.into()
}



